package com.tianyadu.mmall.core.authentication.mobile;

import com.tianyadu.mmall.core.conf.SecurityConstantsConfig;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 短信验证码过滤器
 *
 * @author xueyj
 * @date 2019-11-14 22:19
 */
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    /**
     * 传递短信验证码参数名称
     */
    public static final String MOBILE_KEY = SecurityConstantsConfig.DEFAULT_PARAMETER_NAME_MOBILE;
    private String mobileParameter = MOBILE_KEY;

    /**
     * 当前请求是否只处理post请求
     */
    private boolean postOnly = true;

    /**
     * 拦截请求地址及请求方式
     *
     * @author xueyj
     * @date 2019/11/14-22:21
     */
    public SmsCodeAuthenticationFilter() {
        //  拦截路径及请求方式
        super(new AntPathRequestMatcher(SecurityConstantsConfig.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, SecurityConstantsConfig.METHOD_OF_POST));
    }

    /**
     * @Description: 认证流程
     * @param:
     * @return:
     * @author:
     * @date: 2018/12/13 18:26
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        // 定义请求方式
        String requestType = SecurityConstantsConfig.METHOD_OF_POST;
        //  校验当前请求方式是否为post
        if (postOnly && !requestType.equals(request.getMethod())) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        // 获取短息验证码信息
        String mobile = obtainMobile(request);
        if (mobile == null) {
            mobile = "";
        }
        mobile = mobile.trim();
        //  实例化短信验证码认证token
        SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
        // 将认证信息添加至token
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }


    /**
     * 获取手机号
     */
    protected String obtainMobile(HttpServletRequest request) {
        return request.getParameter(mobileParameter);
    }

    /**
     * 设置请求详情，如请求ip，sessionId等,添加至认证请求中
     * <p>
     * Provided so that subclasses may configure what is put into the
     * authentication request's details property.
     *
     * @param request     that an authentication request is being created for
     * @param authRequest the authentication request object that should have its details
     *                    set
     */
    protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    /**
     * Defines whether only HTTP POST requests will be allowed by this filter.
     * If set to true, and an authentication request is received which is not a
     * POST request, an exception will be raised immediately and authentication
     * will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method
     * will be called as if handling a failed authentication.
     * <p>
     * Defaults to <tt>true</tt> but may be overridden by subclasses.
     */
    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public final String getMobileParameter() {
        return mobileParameter;
    }

    /**
     * Sets the parameter name which will be used to obtain the username from
     * the login request.
     *
     * @param usernameParameter the parameter name. Defaults to "username".
     */
    public void setMobileParameter(String usernameParameter) {
        Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
        this.mobileParameter = usernameParameter;
    }

}
